/*
 * Copyright 2011 CraneLeeon authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.crane.webtemplate.action;

import com.opensymphony.xwork2.ActionSupport;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.sql.SQLException;
import java.text.ParseException;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts2.interceptor.ServletRequestAware;
import org.apache.struts2.interceptor.ServletResponseAware;
import org.apache.struts2.interceptor.SessionAware;
import org.crane.webtemplate.FormatSpec;
import org.crane.webtemplate.dao.BaseDAO;
import org.crane.webtemplate.dao.Filter;
import org.crane.webtemplate.dao.HibernateUtil;
import org.crane.webtemplate.model.BO;
import org.crane.webtemplate.model.choice.BasicChoice;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

/**
 *
 * @author CraneLeeon
 */
public abstract class BaseAction extends ActionSupport implements SessionAware, 
                                                                ServletRequestAware,
                                                                ServletResponseAware{

    private static final Log logger = LogFactory.getLog(BaseAction.class);
    public static final String SEARCH_OPER_EQ = "eq";
    public static final String SEARCH_OPER_NE = "ne";
    public static final String SEARCH_OPER_LT = "lt";
    public static final String SEARCH_OPER_LE = "le";
    public static final String SEARCH_OPER_GT = "gt";
    public static final String SEARCH_OPER_GE = "ge";
    public static final String SEARCH_OPER_BW = "bw";
    public static final String SEARCH_OPER_BN = "bn";
    public static final String SEARCH_OPER_EW = "ew";
    public static final String SEARCH_OPER_EN = "en";
    public static final String SEARCH_OPER_CN = "cn";
    public static final String SEARCH_OPER_NC = "nc";
    public static final String SEARCH_OPER_IN = "in";
    public static final String SEARCH_OPER_NI = "ni";
    public static final String REGEX_SEARCHSTRING = "([\u4e00-\u9fa5]|[0-9]|[a-z]|[A-Z]|[ \\.,_-])+";
    protected static final Map<String, String> searchOperHelper = new HashMap();
    protected static final Map<String, String[]> whereParaMarkHelper = new HashMap();

    static {
        searchOperHelper.put(SEARCH_OPER_EQ, "=");
        searchOperHelper.put(SEARCH_OPER_NE, "!=");
        searchOperHelper.put(SEARCH_OPER_LT, "<");
        searchOperHelper.put(SEARCH_OPER_LE, "<=");
        searchOperHelper.put(SEARCH_OPER_GT, ">");
        searchOperHelper.put(SEARCH_OPER_GE, ">=");
        searchOperHelper.put(SEARCH_OPER_BW, "like");
        searchOperHelper.put(SEARCH_OPER_BN, "not like");
        searchOperHelper.put(SEARCH_OPER_EW, "like");
        searchOperHelper.put(SEARCH_OPER_EN, "not like");
        searchOperHelper.put(SEARCH_OPER_CN, "like");
        searchOperHelper.put(SEARCH_OPER_NC, "not like");
        searchOperHelper.put(SEARCH_OPER_IN, "in");
        searchOperHelper.put(SEARCH_OPER_NI, "not in");

        whereParaMarkHelper.put(SEARCH_OPER_EQ, new String[]{"", ""});
        whereParaMarkHelper.put(SEARCH_OPER_NE, new String[]{"", ""});
        whereParaMarkHelper.put(SEARCH_OPER_LT, new String[]{"", ""});
        whereParaMarkHelper.put(SEARCH_OPER_LE, new String[]{"", ""});
        whereParaMarkHelper.put(SEARCH_OPER_GT, new String[]{"", ""});
        whereParaMarkHelper.put(SEARCH_OPER_GE, new String[]{"", ""});
        whereParaMarkHelper.put(SEARCH_OPER_BW, new String[]{"", "%"});
        whereParaMarkHelper.put(SEARCH_OPER_BN, new String[]{"", "%"});
        whereParaMarkHelper.put(SEARCH_OPER_EW, new String[]{"%", ""});
        whereParaMarkHelper.put(SEARCH_OPER_EN, new String[]{"%", ""});
        whereParaMarkHelper.put(SEARCH_OPER_CN, new String[]{"%", "%"});
        whereParaMarkHelper.put(SEARCH_OPER_NC, new String[]{"%", "%"});
        whereParaMarkHelper.put(SEARCH_OPER_IN, new String[]{"'", "'"});
        whereParaMarkHelper.put(SEARCH_OPER_NI, new String[]{"'", "'"});

    }
    
    public static enum UpdatePriciple{
        FORBIDDEN_ORIENTED,
        ALLOWABLE_ORIENTED;
    }
    
    protected int act = -99;
    protected int page = 1;
    protected int rows = 10;
    protected String sidx = "id";
    protected String sord = "asc";
    protected String searchField;
    protected String searchOper;
    protected String searchString;
    protected String[] searchStringArray;
    protected Map session;
    protected HttpServletRequest servletRequest;
    protected HttpServletResponse servletResponse;
    protected String json;
    protected String xml;
    protected String csv;
    protected Long id;
    private Long[] ids;
    private String[] fieldsForList;
    private boolean cellDataAsJson = false;
    private Map<String, String> fieldNamesMap = new HashMap<String, String>();

    public Map<String, String> getFieldNamesMap() {
        return fieldNamesMap;
    }

    public void setFieldNamesMap(Map<String, String> fieldNamesMap) {
        this.fieldNamesMap = fieldNamesMap;
    }

    public void setId(Long id) {
        if (id == null || id < 1) {
            return;
        }
        this.id = id;
    }

    public Long[] getIds() {
        return this.ids;
    }

    public void setIds(String ids) {
        if (ids != null && !ids.isEmpty()) {
            String[] idarr = ids.split(",");
            Long[] longarr = new Long[idarr.length];
            for (int i = 0; i < idarr.length; i++) {
                longarr[i] = Long.valueOf(idarr[i].trim());
            }
            this.ids = longarr;
        } else {
            return;
        }
    }

    public boolean isCellDataAsJson() {
        return cellDataAsJson;
    }

    public void setCellDataAsJson(boolean cellDataAsJson) {
        this.cellDataAsJson = cellDataAsJson;
    }

    public String[] getFieldsForList() {
        return this.fieldsForList;
    }

    public void setFieldsForList(String fieldsForList) {
        if (fieldsForList != null && !fieldsForList.isEmpty()) {
            String[] fields = fieldsForList.split(",");
            for (int i = 0; i < fields.length; i++) {
                fields[i] = fields[i].trim();
            }
            this.fieldsForList = fields;
        } else {
            return;
        }
    }
    
    /***
     * for url on the page to getBO context path
     * @return 
     */
    public String getHomeURL(){
        return this.servletRequest.getContextPath();
    }

    @Override
    public void setSession(Map<String, Object> map) {
        this.session = map;
    }

    @Override
    public void setServletRequest(HttpServletRequest servletResponse){
        this.servletRequest = servletResponse;
    }

    @Override
    public void setServletResponse(HttpServletResponse servletResponse) {
        this.servletResponse = servletResponse;
    }
    
    public int getAct() {
        return act;
    }

    public void setAct(int act) {
        this.act = act;
    }

    public int getPage() {
        return page;
    }

    public void setPage(int page) {
        this.page = page;
    }

    public int getOffset() {
        int re = (this.page - 1) * this.rows;
        return re < 0 ? 0 : re;
    }

    public int getRows() {
        return rows;
    }

    public void setRows(int rows) {
        if(rows <= 1000){
            this.rows = rows;
        }else{
            this.rows = 1000;
        }
    }

    public String getSidx() {
        return sidx;
    }

    public void setSidx(String sidx) {
        if (Pattern.matches("([0-9]|[a-z]|[A-Z]|[_\\.\\,])+", sidx)) {
            this.sidx = sidx;
        }
    }

    public String getSord() {
        return sord;
    }

    public void setSord(String sord) {
        if (sord.startsWith("asc") || sord.startsWith("desc")) {
            this.sord = sord;
        }
    }

    public String getSearchField() {
        return searchField;
    }

    public void setSearchField(String searchField) {
        if (Pattern.matches("([0-9]|[a-z]|[A-Z]|[_\\.])+", searchField)) {
            this.searchField = searchField;
        }
    }

    public String getSearchOper() {
        return searchOper;
    }

    public void fixHQLWhere(Filter filter) throws Exception {
        if (this.searchField == null || this.searchOper == null || this.searchString == null
                || this.searchField.isEmpty() || this.searchOper.isEmpty() || this.searchString.isEmpty()) {
            return;
        }
        String _oper = this.getSearchOperAsHQL();
        if (_oper.indexOf("in") > -1) {
            searchStringArray = this.searchString.split(",");
        } else {
            searchStringArray = new String[]{this.searchString};
        }
        Object[] _param = (Object[]) this.getSearchParam(this.getBoClass(), this.searchField);
        if (_oper.indexOf("in") > -1) {
            boolean in = _oper.indexOf("not") < 0 ? true : false;
            Filter.InOrNotIn[] ini = new Filter.InOrNotIn[]{new Filter.InOrNotIn(this.searchField, in, _param)};
            filter.setInOrNotIns(Arrays.asList(ini));
        } else {
            Object p = _param[0];
            if (p instanceof String) {
                String[] mark = this.getWhereParamMark();
                filter.setWhereParams(new Object[]{mark[0] + p + mark[1]});
            } else {
                if (_oper.indexOf("like") > -1) {//'like'/'in' only supports String
                    filter.setWhereStr("1=2");// never getBO matched records
                    return;
                }
                filter.setWhereParams(new Object[]{p});
            }
            StringBuffer _where = new StringBuffer();
            _where.append(this.getBoClass().getName().replaceAll("\\.", "_") + "." + this.searchField + " ");
            _where.append(_oper);
            _where.append(" (?) ");
            filter.setWhereStr(_where.toString());
        }

    }

    protected String getSearchOperAsHQL() {
        return searchOperHelper.get(searchOper);
    }

    private String[] getWhereParamMark() {
        return whereParaMarkHelper.get(searchOper);
    }

    public void setSearchOper(String searchOper) {
        if (Pattern.matches("(eq|ne|lt|le|gt|ge|bw|bn|ew|en|cn|nc|in|ni)+", searchOper)) {
            this.searchOper = searchOper;
        }
    }

    public String getSearchString() {
        return searchString;
    }

    public abstract Class getBoClass();

    private Field getExactlyField(Class ownercls, String fieldname) throws NoSuchFieldException {
        try {
            ownercls = ownercls.getSuperclass();
            if (ownercls == null) {
                throw new NoSuchFieldException();
            }
            return ownercls.getDeclaredField(fieldname);
        } catch (NoSuchFieldException ex) {
            return getExactlyField(ownercls, fieldname);
        }
    }

    /***
     * convert searchString/searchStringArray to appropriate type, 
     * this can be overide in the inherited class if u need to getBO specified params
     * @return
     * @throws Exception
     */
    public Object[] getSearchParam(Class ownercls, String fieldname) throws Exception {
        if (fieldname.indexOf(".") > -1) {
            int index = fieldname.indexOf(".");
            String subfieldname = fieldname.substring(index + 1);
            fieldname = fieldname.substring(0, fieldname.indexOf("."));
            try {
                ownercls = ownercls.getDeclaredField(fieldname).getType();
            } catch (NoSuchFieldException noSuchFieldException) {
                ownercls = getExactlyField(ownercls, fieldname).getType();
            }
            return getSearchParam(ownercls, subfieldname);
        }
        Field field = null;
        try {
            field = ownercls.getDeclaredField(fieldname);
        } catch (NoSuchFieldException noSuchFieldException) {
            field = getExactlyField(ownercls, fieldname);
        }
//        for(Field field : ownercls.getDeclaredFields()){
//        if(field.getName().equalsIgnoreCase(fieldname)){
        Class cls = field.getType();
        List params = new ArrayList();
        if (cls.equals(String.class)) {
            return this.searchStringArray;
        } else if (cls.equals(Integer.class)) {
            for (String str : this.searchStringArray) {
                params.add(Integer.valueOf(str));
            }
        } else if (cls.equals(Long.class)) {
            for (String str : this.searchStringArray) {
                params.add(Long.valueOf(str));
            }
        } else if (cls.equals(Float.class)) {
            for (String str : this.searchStringArray) {
                params.add(Float.valueOf(str));
            }
        } else if (cls.equals(Double.class)) {
            for (String str : this.searchStringArray) {
                params.add(Double.valueOf(str));
            }
        } else if (cls.equals(BigDecimal.class)) {
            for (String str : this.searchStringArray) {
                params.add(new BigDecimal(str));
            }
        } else if (cls.equals(Date.class)) {
            for (String str : this.searchStringArray) {
                params.add(FormatSpec.FORMATTER_DATE.parse(str));
            }
        } else if (cls.equals(Boolean.class)) {
            for (String str : this.searchStringArray) {
                params.add(Boolean.valueOf(str));
            }
        }
        return params.toArray();
//        }
//        }
//        return this.searchStringArray;
    }

    public void setSearchString(String searchString) {
        if (Pattern.matches(REGEX_SEARCHSTRING, searchString)) {
            this.searchString = searchString;
        }
    }

    protected BaseDAO getDao() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        String daoname = this.getBoClass().getName().replaceFirst("org.crane.webtemplate.model", "org.crane.webtemplate.dao") + "DAO";
        return (BaseDAO) Class.forName(daoname).newInstance();
    }

    protected Object getSubObject(Object obj, String[] cascadefield) throws NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        if(obj == null) return JSONObject.NULL;
        Object step = null;
        try{
            if(List.class.isAssignableFrom(obj.getClass()) && cascadefield[0].equals("size")){//for listAsJSON size
                step = obj.getClass().getMethod("size").invoke(obj);
            }else{
                try {
                    step = obj.getClass().getMethod("get" + cascadefield[0].substring(0, 1).toUpperCase() + cascadefield[0].substring(1)).invoke(obj);
                } catch (NoSuchMethodException noSuchMethodException) {
                    //Boolean fields use isXxx mostly.
                    step = obj.getClass().getMethod("is" + cascadefield[0].substring(0, 1).toUpperCase() + cascadefield[0].substring(1)).invoke(obj);
                }
            }
        }catch(Exception ex){

        }
        if (cascadefield.length > 1) {
            step = this.getSubObject(step, Arrays.copyOfRange(cascadefield, 1, cascadefield.length));
        }
        if("password".equalsIgnoreCase(cascadefield[0]))
            return "********";//never show password to the client
        if(step == null) return JSONObject.NULL;
        return step;
    }

    protected JSONObject parseBOListForPagedShow(List orignallist, long allRecordsCount) throws Exception {
        JSONArray jsons = new JSONArray();
        String[] fieldNameList = this.getFieldsForList();
        for(BO obj: (List<BO>)orignallist){
            if (fieldNameList == null) {
                jsons.put(new JSONObject().put("id", obj.getId()).put("cell", new Object[]{
                            obj.getId()
                        }));
            }else{
                JSONObject row = new JSONObject().put("id", obj.getId());
                if (isCellDataAsJson()) {
                    row.put("cell", packObjectAsJSONObject(obj, fieldNameList));
                }else{
                    
                    row.put("cell", packObjectAsArray(obj, fieldNameList));
                }
                jsons.put(row);
            }
        }
        JSONObject rejson = new JSONObject().put("page", this.page)//current page index
                .put("total", (this.rows > -1) ? (allRecordsCount / this.rows + ((allRecordsCount % this.rows) > 0 ? 1 : 0)) : 1)//total pages
                .put("records", allRecordsCount)//total records
                .put("rows", jsons).put("success", true).put("info", "");
        return rejson;
    }

    protected JSONObject packObjectAsJSONObject(Object obj, String[] fieldNameList) throws Exception{
        JSONObject rfs = new JSONObject();
        for (String fieldName : fieldNameList) {
            String[] cascade = fieldName.split("\\.");
            Object dataobj = this.getSubObject(obj, cascade);
            if (dataobj instanceof BasicChoice) {
                rfs.put(fieldName, this.getText(dataobj.toString())+"="+((BasicChoice)dataobj).getIntvalue());
                rfs.put(fieldName+".intvalue", ((BasicChoice)dataobj).getIntvalue());
            } else if(dataobj instanceof Date){
                if (fieldName.startsWith("dt_")) {
                    rfs.put(fieldName, FormatSpec.FORMATTER_DATE.format(dataobj));
                } else if (fieldName.startsWith("dtt_")) {
                    rfs.put(fieldName, FormatSpec.FORMATTER_DATETIME.format(dataobj));
                } else if (fieldName.startsWith("t_")) {
                    rfs.put(fieldName, FormatSpec.FORMATTER_TIME.format(dataobj));
                } else if (fieldName.startsWith("ts_")) {
                    rfs.put(fieldName, FormatSpec.FORMATTER_TIMESTAMP.format(dataobj));
                } else {
                    rfs.put(fieldName, FormatSpec.FORMATTER_TIMESTAMP.format(dataobj));
                }
            }else {
                if(this.fieldNamesMap != null){
                    String replacename = this.fieldNamesMap.get(fieldName);
                    if(replacename != null) rfs.put(replacename, dataobj);
                }
                rfs.put(fieldName, dataobj);
            }
        }
        return rfs;
    }

    protected Object[] packObjectAsArray(Object obj, String[] fieldNameList) throws Exception{
        ArrayList<Object> rfs = new ArrayList<Object>();
        for (String field : fieldNameList) {
            String[] cascade = field.split("\\.");
            Object dataobj = this.getSubObject(obj, cascade);
            if (dataobj instanceof BasicChoice) {
                rfs.add(this.getText(dataobj.toString())+"="+((BasicChoice)dataobj).getIntvalue());
            } else if(dataobj instanceof Date){
                if (field.startsWith("dt_")) {
                    rfs.add(FormatSpec.FORMATTER_DATE.format(dataobj));
                } else if (field.startsWith("dtt_")) {
                    rfs.add(FormatSpec.FORMATTER_DATETIME.format(dataobj));
                } else if (field.startsWith("t_")) {
                    rfs.add(FormatSpec.FORMATTER_TIME.format(dataobj));
                } else if (field.startsWith("ts_")) {
                    rfs.add(FormatSpec.FORMATTER_TIMESTAMP.format(dataobj));
                } else {
                    rfs.add(FormatSpec.FORMATTER_TIMESTAMP.format(dataobj));
                }
            }else {
                rfs.add(dataobj);
            }
        }
        return rfs.toArray();
    }
    
    protected Object getBO() throws Exception {
        if (this.getBoClass() == null) {
            throw new Exception("No boClass, please set boClass");
        }
        if (this.id == null) {
            throw new Exception("No id, please set id");
        }
        BaseDAO dao = this.getDao();
        BO obj = dao.get(id);
        return obj;
    }
    
    protected JSONObject getBOAsJSON() throws Exception {
        BO obj = (BO) this.getBO();
        String[] fieldNameList = this.getFieldsForList();
        JSONObject rejson = new JSONObject().put("success", true).put("info", "")
                .put("id", id)
                .put("cell", packObjectAsJSONObject(obj, fieldNameList));
        return rejson;
    }

    /**
     * if use this method not needs to use 'count' method again.
     * @return 2 items object array: 1st is the paged data List<BO>,
     * 2nd is the records count for all(no paged) records which meet this condition
     */
    protected Object[] list() throws Exception {
        if (this.getBoClass() == null) {
            throw new Exception("No boClass, please set boClass");
        }
//        JSONArray jsons = new JSONArray();
        BaseDAO dao = this.getDao();
        Filter filter = new Filter();
        this.fixHQLWhere(filter);
        long records = dao.count(filter);
        List<BO> list = null;
        if (records > 0) {
            if (this.rows > -1) {
                filter.setLimit(this.rows);
            } else {
                //TODO: set default limit if necessary else no limit at all
            }
            filter.setOffset(this.getOffset());
            if(this.sidx != null && this.sidx.length() > 0 && this.sord != null && this.sord.length() > 0){
                String[] sidxs = this.sidx.split(",");
                String[] sords = this.sord.split(",");
                filter.setSorters(new ArrayList<Map.Entry<String, String>>());
                for(int i=0; i < sidxs.length; i++){
                    filter.getSorters().add(new AbstractMap.SimpleEntry<String, String>(sidxs[i], sords[i]));
                }
            }
            list = dao.list(filter);
        }else{
            list = new ArrayList<BO>();
        }
        return new Object[]{list, records};
    }

    protected long count() throws Exception {
        if (this.getBoClass() == null) {
            throw new Exception("No boClass, please set boClass first");
        }
//        JSONArray jsons = new JSONArray();
        BaseDAO dao = this.getDao();
        Filter filter = new Filter();
        this.fixHQLWhere(filter);
        return dao.count(filter);
    }

    protected JSONObject listAsJSON() throws Exception {
        Object[] redata = this.list();
        return this.parseBOListForPagedShow((List)redata[0], (Long)redata[1]);
    }

    protected void delete() throws Exception {
        JSONObject jobj = new JSONObject().put("success", true).put("info", "");
        Long[] _ids = this.getIds();
        if (_ids != null && _ids.length > 0) {
            BaseDAO dao = this.getDao();
            try {
                dao.startTransaction();
                for (Long did : _ids) {
                    if (did > 0) {
                        dao.delete(dao.get(did));
                    }
                }
                dao.commitTransaction();
            } catch (SQLException sQLException) {
                dao.rollbackTransaction();
                throw sQLException;
            }
        }
        this.json = jobj.toString();
    }

    protected Object getDummy(Class type, UpdatePriciple priciple, List<String> fieldNamesForUpdatePriciple) throws JSONException, InstantiationException, IllegalAccessException, ParseException, ClassNotFoundException, SQLException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, NoSuchFieldException {
        JSONObject jsonparam = this.getJsonObject();
        Object obj = null;
        Method[] mthds = type.getMethods();
        List<String> names = Arrays.asList(JSONObject.getNames(jsonparam));
        boolean is_new = true;
        //NOTE: this operation contains potiantial risk to expose all data to be changeable to the client side!!!!
        try {
            Long v_id = (this.id!=null) ? this.id : jsonparam.getLong("id");
            if(v_id > 0){//update
                obj = HibernateUtil.getCurrentSession().get(type, v_id);
                is_new = false;
            }else{
                obj = type.newInstance();
            }
        } catch (JSONException jSONException) {
            obj = type.newInstance();
        }
//        names.remove("id");
        //----------------------------------------------------------------------------------------------------!!!!

        for (Method mthd : mthds) {
//        for (String mname : names) {
            String mname = mthd.getName();
            if (!mname.startsWith("set")) {
                continue;
            }
            mname = mname.replaceFirst("set", "");
            mname = mname.substring(0, 1).toLowerCase() + mname.substring(1);
//            mname = "set"+mname.substring(0, 1).toUpperCase() + mname.substring(1);
//            Method mthd = type.getMethod(mname, type.getField(mname).getClass());
            if (!mname.equalsIgnoreCase("id") && names.contains(mname)) {
                //-------------------------------------------!!!!
                if(!is_new && fieldNamesForUpdatePriciple!=null){
                    switch(priciple){
                        case FORBIDDEN_ORIENTED:if(fieldNamesForUpdatePriciple.contains(mname)){continue;} break;
                        case ALLOWABLE_ORIENTED:if(!fieldNamesForUpdatePriciple.contains(mname)){continue;} break;
                    }
                }
                //-------------------------------------------!!!!
                Object val = null;
                Class paramcls = mthd.getParameterTypes()[0];

                if (JSONObject.NULL.equals(jsonparam.get(mname))) {
                    //do nothing. null will be set
                } else if(jsonparam.get(mname).toString().length()==0){
                    val = null;
                } else if (paramcls.equals(Boolean.class)) {
                    val = jsonparam.getBoolean(mname);
                } else if (paramcls.equals(Integer.class)) {
                    val = jsonparam.getInt(mname);
                } else if (paramcls.equals(Long.class)) {
                    val = jsonparam.getLong(mname);
                    if (mname.equalsIgnoreCase("id") && Long.class.cast(val) < 1) {
                        continue;
                    }
                } else if (paramcls.equals(String.class)) {
                    val = jsonparam.getString(mname);
                } else if (paramcls.equals(BigDecimal.class)) {
                    val = new BigDecimal(jsonparam.getString(mname));
                } else if (paramcls.equals(Double.class)) {
                    val = jsonparam.getDouble(mname);
                } else if (paramcls.equals(Float.class)) {
                    val = Float.valueOf(jsonparam.getString(mname));
                } else if (paramcls.equals(Date.class)) {
                    if (mname.startsWith("dt_")) {
                        val = FormatSpec.FORMATTER_DATE.parse(jsonparam.getString(mname));
                    } else if (mname.startsWith("dtt_")) {
                        val = FormatSpec.FORMATTER_DATETIME.parse(jsonparam.getString(mname));
                    } else if (mname.startsWith("t_")) {
                        val = FormatSpec.FORMATTER_TIME.parse(jsonparam.getString(mname));
                    } else if (mname.startsWith("ts_")) {
                        val = FormatSpec.FORMATTER_TIMESTAMP.parse(jsonparam.getString(mname));
                    } else {
                        val = FormatSpec.FORMATTER_TIMESTAMP.parse(jsonparam.getString(mname));
                    }
                } else if (BO.class.isAssignableFrom(paramcls)) {
                    String daoname = paramcls.getName().replaceFirst("org.crane.webtemplate.model", "org.crane.webtemplate.dao") + "DAO";
                    BaseDAO dao = (BaseDAO) Class.forName(daoname).newInstance();
                    val = dao.get(jsonparam.getLong(mname));
                } else {
                    continue;
                }
                mthd.invoke(obj, val);
            }

        }

        return obj;
    }

    public String getJson() {
        return json;
    }

    public JSONObject getJsonObject() throws JSONException {
        if (this.json == null) {
            return null;
        }
        return new JSONObject(json);
    }

    public void setJson(String json) {
        this.json = json;
    }

    public String getXml() {
        return xml;
    }

    public void setXml(String xml) {
        this.xml = xml;
    }

    public String getCsv() {
        return csv;
    }

    public void setCsv(String csv) {
        this.csv = csv;
    }
    
    protected void save() throws Exception {
        BaseDAO dao = this.getDao();
        JSONObject jobj = new JSONObject().put("success", true).put("info", "");
        dao.startTransaction();
        try {
            BO obj = (BO) this.getDummy(
                        this.getBoClass(), 
                        UpdatePriciple.FORBIDDEN_ORIENTED,
                        null
                    );
            dao.createOrUpdate(obj); 
            //-------
            jobj.put("id", obj.getId());
            dao.commitTransaction();
        } catch (SQLException sQLException) {
            dao.rollbackTransaction();
            throw sQLException;
        }
        this.json = jobj.toString();
    }
    
}
